package fcm

import (
	"net/http"
	"bytes"
	"xlq-demo/library/logger"
	"time"
	"io/ioutil"
	"github.com/dgrijalva/jwt-go"
	"encoding/json"
	"fmt"
)

//初始化
func Initial() {
	//解析json
	getFcmJson()

	//定时刷新accessToken
	fleshAccessToken()

}

//发送
type SendIn struct {
	Token string `json:"token"`
	Title string `json:"title"`
	Content string `json:"content"`
	Extra string `json:"extra"`
}
type FcmSendBody struct {
	Message FcmSendBodyMsg `json:"message"`
}
type FcmSendBodyMsg struct {
	Token string `json:"token"`
	Notification FcmSendBodyMsgNotification `json:"notification"`
	Data map[string]string `json:"data"`
	Apns interface{} `json:"apns"`
	Android interface{} `json:"android"`
}
type FcmSendBodyMsgNotification struct {
	Title string `json:"title"`
	Body string `json:"body"`
}
func Send(in *SendIn) {
	if in.Token == "" {
		logger.Error("token is nil")
		return
	}

	sendReq := FcmSendBody {
		Message: FcmSendBodyMsg{
			Token: in.Token,
			Notification: FcmSendBodyMsgNotification{
				Title: in.Title,
				Body: in.Content,
			},
			Data: map[string]string{
				"extra": in.Extra,
			},
			Apns: map[string]interface{}{
				"payload": map[string]interface{}{
					"aps": map[string]interface{}{
						"badge": 1,
						"content-available": 1,
						"sound": "chime.aiff",
					},
				},
			},
			Android: map[string]interface{}{
				"notification": map[string]interface{}{
					"sound": "res_raw_notification_sound.mp3",
				},
			},
		},
	}

	sendReqBody,err := json.Marshal(sendReq)
	if err != nil {
		logger.Error(err.Error())
		return
	}

	//发起请求
	res,err := httpRequest(&httpRequestIn{
		Method: "POST",
		Url: "https://fcm.googleapis.com/v1/projects/"+fcmJson.ProjectId+"/messages:send",
		Body: sendReqBody,
		Header: map[string]string{
			"Authorization": "Bearer " + accessTokenData.AccessToken,
			"Content-Type": "application/json",
			"Content-Length": fmt.Sprintf("%d",len(sendReqBody)),
			"Host": "fcm.googleapis.com",
		},
	})
	if err != nil {
		logger.Error(err.Error())
		fmt.Println("send:",string(res))
		return
	}

	fmt.Println("send:",string(res))
}


//配置信息
var fcmJson FcmJson
type FcmJson struct {
	Type string `json:"type"`
	ProjectId string `json:"project_id"`
	PrivateKeyId string `json:"private_key_id"`
	PrivateKey string `json:"private_key"`
	ClientEmail string `json:"client_email"`
	ClientId string `json:"client_id"`
	AuthUri string `json:"auth_uri"`
	TokenUri string `json:"token_uri"`
	AuthProviderX509CertUrl string `json:"auth_provider_x509_cert_url"`
	ClientX509CertUrl string `json:"client_x509_cert_url"`
}
//accessToken
var accessTokenData AccessTokenData
type AccessTokenData struct {
	AccessToken string
	ExpireAt int64 //过期时间点
}

//解析fcmJson
func getFcmJson() {
	// 解析密钥
	bys,err := ioutil.ReadFile("./conf/sa.json")
	if err != nil {
		logger.Error(err.Error())
	}

	err = json.Unmarshal(bys,&fcmJson)
	if err != nil {
		logger.Error(err.Error())
	}
}

//定时刷新
func fleshAccessToken() {
	go func() {
		for {
			if accessTokenData.ExpireAt - 10 < time.Now().Unix() {
				getAccessToken()
			}
			time.Sleep(3*time.Second)
		}
	}()

}

// token数据响应
type AccessTokenRes struct {
	AccessToken string `json:"access_token"`
	ExpiresIn int64 `json:"expires_in"`
	TokenType string `json:"token_type"`
}
//获取accessToken
func getAccessToken() error {
	//获取jwt
	timeNow := time.Now().Unix()

	//生成accessToken
	token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
		"iss": fcmJson.ClientEmail,
		"scope": "https://www.googleapis.com/auth/firebase.messaging",
		"aud": fcmJson.TokenUri,
		"iat": timeNow,
		"exp": timeNow + (60*60),  // Maximum expiration time is one hour
	})

	priKey,err := jwt.ParseRSAPrivateKeyFromPEM([]byte(fcmJson.PrivateKey))
	if err != nil {
		logger.Error(err.Error())
	}


	tokenString, err := token.SignedString(priKey)
	if err != nil {
		logger.Error(err.Error())
	}

	reqBody := []byte(fmt.Sprintf("grant_type=%s&assertion=%s","urn:ietf:params:oauth:grant-type:jwt-bearer",tokenString))
	reqIn := &httpRequestIn{
		Method: "POST",
		Url: fcmJson.TokenUri,
		Body: reqBody,
		Header: map[string]string{
			"Content-Type": "application/x-www-form-urlencoded",
			"Content-Length": fmt.Sprintf("%d",len(reqBody)),
			"Host": "oauth2.googleapis.com",
		},
	}

	resBody,err := httpRequest(reqIn)
	if err != nil {
		logger.Error(err.Error())
		return err
	}

	var res AccessTokenRes
	err = json.Unmarshal(resBody,&res)
	if err != nil {
		logger.Error(err.Error())
		return err
	}

	accessTokenData.AccessToken = res.AccessToken
	accessTokenData.ExpireAt = timeNow + res.ExpiresIn
	return nil
}

//http 请求 method,url,body,header
type httpRequestIn struct {
	Method string
	Url string
	Body []byte
	Header map[string]string
}
func httpRequest(in *httpRequestIn) ([]byte,error) {
	req,err := http.NewRequest(in.Method,in.Url,bytes.NewReader(in.Body))
	if err != nil {
		logger.Error(err)
		return nil,err
	}
	//添加header
	for key,val := range in.Header {
		req.Header.Set(key,val)
	}

	/*urlproxy,err := url.Parse("http://47.241.4.152:10000")
	if err != nil {
		logger.Error(err)
		return nil,err
	}*/
	client := http.Client{
		/*Transport: &http.Transport{
			Proxy: http.ProxyURL(urlproxy),
		},*/
		//Timeout: 5*time.Second,
	}

	res,err := client.Do(req)
	if err != nil {
		logger.Error(err)
		return nil,err
	}

	resBody,err := ioutil.ReadAll(res.Body)
	res.Body.Close()

	httpCode := res.StatusCode
	if httpCode != http.StatusOK {
		logger.Error("http error:",httpCode,in.Url)
		fmt.Println(string(resBody))
		return nil,fmt.Errorf("http error")
	}

	return resBody,nil
}


